library(tidyverse)     # for graphing and data cleaning
## ── Attaching packages ──────────────────────────────────────────────────────────────────────────────────── tidyverse 1.3.0 ──
## ✓ ggplot2 3.3.2     ✓ purrr   0.3.4
## ✓ tibble  3.0.3     ✓ dplyr   1.0.1
## ✓ tidyr   1.1.1     ✓ stringr 1.4.0
## ✓ readr   1.3.1     ✓ forcats 0.5.0
## ── Conflicts ─────────────────────────────────────────────────────────────────────────────────────── tidyverse_conflicts() ──
## x dplyr::filter() masks stats::filter()
## x dplyr::lag()    masks stats::lag()
library(googlesheets4) # for reading googlesheet data
library(lubridate)     # for date manipulation
## 
## Attaching package: 'lubridate'
## The following objects are masked from 'package:base':
## 
##     date, intersect, setdiff, union
library(ggthemes)      # for even more plotting themes
library(babynames)     # for using the babynames dataset
library(geofacet)      # for special faceting with US map layout
gs4_deauth()           # To not have to authorize each time you knit.
theme_set(theme_minimal())       # My favorite ggplot() theme :)
#Lisa's garden data
garden_harvest <- read_sheet("https://docs.google.com/spreadsheets/d/1DekSazCzKqPS2jnGhKue7tLxRU3GVL1oxi-4bEM5IWw/edit?usp=sharing") %>% 
  mutate(date = ymd(date))
## Reading from "2020_harvest"
## Range "Sheet1"
#COVID-19 data from the New York Times
covid19 <- read_csv("https://raw.githubusercontent.com/nytimes/covid-19-data/master/us-states.csv")
## Parsed with column specification:
## cols(
##   date = col_date(format = ""),
##   state = col_character(),
##   fips = col_character(),
##   cases = col_double(),
##   deaths = col_double()
## )

Instructions

  • Put your name at the top of the document.

  • For ALL graphs, you should include appropriate labels.

  • Feel free to change the default theme, which I currently have set to theme_minimal().

  • Use good coding practice. Read the short sections on good code with pipes and ggplot2.

  • When you are finished with ALL the exercises, uncomment the options at the top so your document looks nicer. Don’t do it before then, or else you might miss some important warnings and messages.

Warm-up exercises with garden data

These exercises will reiterate the plotting and data wrangling skills you learned in the ggplot_dplyr tutorial. They will use the garden_harvest data that you are hopefully starting to become familiar with.

  1. Filter the data to lettuce vegetables. Create a histogram of the weight in grams. This shows the distribution of lettuce “harvests”. There may be multiple harvests in the same day since there are multiple varieties of lettuce and I sometimes harvested lettuce more than once a day. For the lettuce, also compute the mean and standard deviation of the harvest (use the summarize() function). Use those to help you describe the distribution of lettuce harvests.
garden_harvest %>% 
  filter(vegetable == "lettuce") %>% 
  group_by(vegetable) %>% 
  summarize(mean = mean(weight), stdev = sd(weight))
## `summarise()` ungrouping output (override with `.groups` argument)
garden_harvest %>%  
  filter(vegetable == "lettuce") %>% 
  group_by(vegetable) %>% 
  ggplot(aes(x = weight)) +
  geom_histogram(fill = "light blue", color = "black") +
  labs(title = "The Weight of Lettuce Harvest",
       x = "Weight (grams)",
       y = "Total Amount")
## `stat_bin()` using `bins = 30`. Pick better value with `binwidth`.

The average weight of lettuce harvest is 76 grams, with a pretty high standard deviation of 56 grams. Most of the time the harvest weight is less than 100 grams.

  1. Create a bar chart that shows the number of times I harvested the different varieties of lettuce. Put the variety on the y-axis and count on x-axis. CHALLENGE: order them so the variety I harvested the most is at the top.
garden_harvest %>% 
  filter(vegetable == "lettuce") %>% 
  group_by(vegetable, variety) %>% 
  summarize(count = n()) %>% 
  arrange(desc(count)) %>% 
  ggplot(aes(x = count, y = fct_reorder(variety, count))) +
  geom_bar(stat = "identity", fill = "darkturquoise", color = "white") + 
  labs(title = "The Harvest of Each Varieties of Lettuce",
       x = "Count",
       y = "Variety")
## `summarise()` regrouping output by 'vegetable' (override with `.groups` argument)

  1. Filter the data to beets. Summarize the data to compute the total weight in grams harvests on each day for each variety of beet. Add two new variables: a. weight in pounds, b. cumulative weight in pounds. Create a line graph that shows the cumulative harvest by day for each variety of beet, with a different color for each variety. Describe what you see. CHALLENGE: make the line colors correspond with the colors of the beets - a reddish-purple color and a dark-yellow color.
garden_harvest %>% 
  filter(vegetable == "beets") %>% 
  group_by(variety, date) %>% 
  mutate(weight_lb = weight * 0.00220462,
         tot_weight_lb = sum(weight_lb),
         cum_weight_lb = cumsum(tot_weight_lb)) %>% 
  ggplot(aes(x = date, y = cum_weight_lb, color = variety)) + 
  geom_line() + 
  scale_color_manual(values=c("#D89000", "#00BA42", "#E76BF3")) +
  labs(title = "Cumulative Harvest by Day for Each Variety of Beet",
       x = "Date",
       y = "Cumulative Weight (lb)")

  1. Summarize the data to compute the daily harvest in pounds for each vegetable. Create side-by-side boxplots to compare the distributions of daily harvests for each vegetable.
garden_harvest %>% 
  group_by(vegetable, date) %>% 
  mutate(weight_lb = weight * 0.00220462) %>% 
  summarise(daily_harvest_lb = sum(weight_lb)) %>% 
  ggplot(aes(x = daily_harvest_lb, y = vegetable)) +
  geom_boxplot() +
  labs(title = "Daily Harvest in Pounds for Each Vegetable",
       x = "Daily Harvest (lb)",
       y = "Vegetable")
## `summarise()` regrouping output by 'vegetable' (override with `.groups` argument)

Babynames exercises

We are going to practice more dplyr and ggplot2 skills on the babynames dataset from the babynames library. First, install the package (unless you’re using the server, in which case it is already installed). Then, in the libraries section at the top, uncomment the line where that library is loaded and re-run that code chunk. BTW, this is my kids’ favorite dataset … yes, my kids have a favorite dataset.

A couple notes about this dataset:

  1. As with many datasets that contain the variable sex, in this dataset it is binary. Hopefully this will change in the future.
  2. Only names with at least 5 uses in a year are included.
  3. The data come from the Social Security Administration. In the past, not everyone needed or was able to obtain a social security number. So those people are not reflected in the data.
  1. Add a new variable to babynames called has2000 that indicates whether the name was used more than 2000 times within each sex and year.
babynames %>% 
  mutate(has2000 = n > 2000)
  1. Add on to the code you wrote above and compute the proportion of names each year that had more than 2000 babies. Do this separately for each sex. TIP: you can use TRUEs and FALSEs mathematically. TRUE = 1 and FALSE = 0.
babynames %>% 
  mutate(has2000 = n > 2000) %>% 
  group_by(year, sex) %>% 
  summarise(above_2k_prop = mean(has2000)) # sum(has2000) / n()
## `summarise()` regrouping output by 'year' (override with `.groups` argument)
  1. Create a line graph from the dataset you created in the previous problem. Year should be along the x-axis, proportion of names with more than 2000 babies is on the y-axis, and there should be a separate line for each sex.
babynames %>% 
  mutate(has2000 = n > 2000) %>% 
  group_by(year, sex) %>% 
  summarise(above_2k_prop = mean(has2000)) %>% 
  ggplot(aes(x = year, y = above_2k_prop, color = sex)) + 
  geom_line() + 
  labs(title = "Proportion of Names with More Than 2000 Babies over Years",
       x = "Year",
       y = "Proportion")
## `summarise()` regrouping output by 'year' (override with `.groups` argument)

  1. Find the most popular names for males, over all years in the data, and ordered by popularity.
babynames %>% 
  filter(sex == "M") %>%
  group_by(name) %>% 
  summarise(total_n = sum(n)) %>% 
  arrange(desc(total_n))
## `summarise()` ungrouping output (override with `.groups` argument)
  1. Find the most popular names for females, over all years in the data, and ordered by popularity.
babynames %>% 
  filter(sex == "F") %>% 
  group_by(name) %>% 
  summarise(total_n = sum(n)) %>% 
  arrange(desc(total_n))
## `summarise()` ungrouping output (override with `.groups` argument)
  1. For each year and sex in the data, find the number of distinct/unique names. For example, if in 1889 all males were named Matthew, Christopher, or John, then there would be 3 distinct names for males in 1889 (in reality, there are more). Graph the results and describe what you see.
babynames %>% 
  group_by(year, sex) %>% 
  summarise(distinct_names = n()) %>% 
  ggplot(aes(x = year, y = distinct_names, color = sex)) + 
  geom_line() +
  labs(title = "Unique Names for Each Year")
## `summarise()` regrouping output by 'year' (override with `.groups` argument)

  1. (CHALLENGE) In this exercise, we want to find out how using “popular” names has changed over time. For each year and sex, find the proportion of names that are in the top 10 names. Use this to create a line graph with a different line for each sex. What do you observe? HINT: Use the top_n() function. Take time to work through the small steps and write out what it is you need to do in words before jumping into the code.
babynames %>% 
  group_by(year, sex) %>% 
  top_n(10, wt = n) %>% 
  summarise(tot_prop = sum(prop)) %>% 
  ggplot(aes(x = year, y = tot_prop, color = sex)) +
  geom_line() + 
  labs(title = "Proportion of Names That Are In The Top 10 Names",
       y = "total prop")
## `summarise()` regrouping output by 'year' (override with `.groups` argument)

COVID-19 exercises

These exercises will use data on coronavirus cases in the US provided by the New York Times, which I have called covid19. You can (and should) read details about the data here. We will be using the state-level data, which includes the cumulative number of cases for each state for each date, starting on the date they had their first case.

In this class, I think it is important the we address and use relevant data, even if it is challenging. That being said, I do not want these data to increase your stress level. If you would prefer not to work with these data, please let me know and I will provide alternative exercises.

  1. Find the date of each state’s first case. Order the states from latest to most recent first cases.
covid19 %>% 
  group_by(state) %>% 
  summarise(first_case = min(date)) %>% 
  arrange(first_case)
## `summarise()` ungrouping output (override with `.groups` argument)
  1. Let’s examine data for Minnesota, Wisconsin, Iowa, North Dakota, and South Dakota. Create a line graph of cumulative case count by date and color the lines by state. What do you observe? Is this a “fair” comparison, do you think?
covid19 %>% 
  filter(state %in% c("Minnesota","Wisconsin","Iowa","North Dakota","South Dakota")) %>% 
  group_by(state, date) %>% 
  summarize(total_case = cumsum(cases)) %>% 
  ggplot(aes(x = date, y = total_case, color = state)) +
  geom_line() + 
  labs(title = "Cumulative Case Count")
## `summarise()` regrouping output by 'state' (override with `.groups` argument)

I saw a model of exponential growth for each state. Maybe it’s not a fair competition because each state has different number of population. A better way is to compare the proportion of cases among population.

  1. Create the same plot as above, but make the y-axis log scale by using the scale_y_log10() function (search for it in Help or run ?scale_y_log10() in the console). What do you observe? Is this a “fair” comparison? Consider this plot and the previous one, how else might you improve them?
covid19 %>% 
  filter(state %in% c("Minnesota","Wisconsin","Iowa","North Dakota","South Dakota")) %>% 
  group_by(state, date) %>% 
  summarize(total_case = cumsum(cases)) %>% 
  ggplot(aes(x = date, y = total_case, color = state)) +
  geom_line() + 
  scale_y_log10() +
  labs(title = "Cumulative Case Count")
## `summarise()` regrouping output by 'state' (override with `.groups` argument)

On the logged y axis, moving one unit is like multiplying by ten. This second graph also reflects the rate of change.

  1. In this exercise we are going to compute some new variables and get set up for the next exercise. For each state, compute a 1-day lag and 7-day lag of cumulative counts (use the lag() function). When you do this, there will be some missing values because at the beginning there are no lags. Use the replace_na() function to replace those missing values with 0’s. Add two new variables: a. a variable that computes the number of new cases for each day, which can be computed by taking the current cases minus the 1-day lag. b. a variable that computes the 7 day average, which can be computed by taking the current cases minus the 7-day lag and dividing that by 7.
covid_lag <-
  covid19 %>% 
    group_by(state) %>% 
    mutate(one_day_lag = lag(cases, 1), 
           seven_day_lag = lag(cases, 7),
           one_day_lag = replace_na(one_day_lag, 0),
           seven_day_lag = replace_na(seven_day_lag, 0),
           newcases_day = cases - one_day_lag,
           seven_day_avg = (cases - seven_day_lag) / 7)

covid_lag
  1. Using the data you created in the previous step (you can %>% that data into ggplot()), create a plot with the following: a. facet by state, b. a line with the number of new cases by date, b. a line with the 7-day average number of new cases by date - make this line blue. What do you observe? How could this graph be improved?
covid_lag %>% 
  ggplot(aes(x = date)) + 
  geom_line(aes(y = newcases_day), color = "red") + 
  geom_line(aes(y = seven_day_avg), color = "blue") +
  facet_wrap(vars(state)) +
  labs(title = "Number of New Cases and 7 Day Avg Number of New Cases by Date")

I find in most state there are just 2 straight lines. This is because while some state has much more population, the y axis for all the states are the same, which should be improved.

  1. For this part, you will need the geofacet library. First, install it (unless you are using the server, in which case it will already be there for you). Then, uncomment the library statement in the 2nd R code chunk above and re-run that R code chunk. Create the same plot as above but instead of regular faceting, use facet_geo(). Also set scales="free" to the facet function. What do you learn from this plot that you didn’t learn from the previous one?
covid_lag %>% 
  ggplot(aes(x = date)) + 
  geom_line(aes(y = newcases_day), color = "red") + 
  geom_line(aes(y = seven_day_avg), color = "blue") +
  facet_geo(vars(state), scales = "free") +
  labs(title = "Number of New Cases and 7 Day Avg Number of New Cases by Date")
## Warning: Using `as.character()` on a quosure is deprecated as of rlang 0.3.0.
## Please use `as_label()` or `as_name()` instead.
## This warning is displayed once per session.
## Some values in the specified facet_geo column 'state' do not match the
##   'name' column of the specified grid and will be removed: Puerto Rico,
##   Virgin Islands, Guam, Northern Mariana Islands

I could also see the geography of each data point in this second graph.

LS0tCnRpdGxlOiAnV2Vla2x5IEV4ZXJjaXNlcyAjMicKYXV0aG9yOiAiS2FpeWFuZyBZYW8iCm91dHB1dDogCiAgaHRtbF9kb2N1bWVudDoKICAgIHRvYzogVFJVRQogICAgdG9jX2Zsb2F0OiBUUlVFCiAgICBkZl9wcmludDogcGFnZWQKICAgIGNvZGVfZG93bmxvYWQ6IHRydWUKLS0tCgoKYGBge3Igc2V0dXAsIGluY2x1ZGU9RkFMU0V9CiNrbml0cjo6b3B0c19jaHVuayRzZXQoZWNobyA9IFRSVUUsIGVycm9yPVRSVUUsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0UpCmBgYAoKYGBge3IgbGlicmFyaWVzfQpsaWJyYXJ5KHRpZHl2ZXJzZSkgICAgICMgZm9yIGdyYXBoaW5nIGFuZCBkYXRhIGNsZWFuaW5nCmxpYnJhcnkoZ29vZ2xlc2hlZXRzNCkgIyBmb3IgcmVhZGluZyBnb29nbGVzaGVldCBkYXRhCmxpYnJhcnkobHVicmlkYXRlKSAgICAgIyBmb3IgZGF0ZSBtYW5pcHVsYXRpb24KbGlicmFyeShnZ3RoZW1lcykgICAgICAjIGZvciBldmVuIG1vcmUgcGxvdHRpbmcgdGhlbWVzCmxpYnJhcnkoYmFieW5hbWVzKSAgICAgIyBmb3IgdXNpbmcgdGhlIGJhYnluYW1lcyBkYXRhc2V0CmxpYnJhcnkoZ2VvZmFjZXQpICAgICAgIyBmb3Igc3BlY2lhbCBmYWNldGluZyB3aXRoIFVTIG1hcCBsYXlvdXQKZ3M0X2RlYXV0aCgpICAgICAgICAgICAjIFRvIG5vdCBoYXZlIHRvIGF1dGhvcml6ZSBlYWNoIHRpbWUgeW91IGtuaXQuCnRoZW1lX3NldCh0aGVtZV9taW5pbWFsKCkpICAgICAgICMgTXkgZmF2b3JpdGUgZ2dwbG90KCkgdGhlbWUgOikKYGBgCgpgYGB7ciBkYXRhfQojTGlzYSdzIGdhcmRlbiBkYXRhCmdhcmRlbl9oYXJ2ZXN0IDwtIHJlYWRfc2hlZXQoImh0dHBzOi8vZG9jcy5nb29nbGUuY29tL3NwcmVhZHNoZWV0cy9kLzFEZWtTYXpDektxUFMyam5HaEt1ZTd0THhSVTNHVkwxb3hpLTRiRU01SVd3L2VkaXQ/dXNwPXNoYXJpbmciKSAlPiUgCiAgbXV0YXRlKGRhdGUgPSB5bWQoZGF0ZSkpCgojQ09WSUQtMTkgZGF0YSBmcm9tIHRoZSBOZXcgWW9yayBUaW1lcwpjb3ZpZDE5IDwtIHJlYWRfY3N2KCJodHRwczovL3Jhdy5naXRodWJ1c2VyY29udGVudC5jb20vbnl0aW1lcy9jb3ZpZC0xOS1kYXRhL21hc3Rlci91cy1zdGF0ZXMuY3N2IikKYGBgCgojIyBJbnN0cnVjdGlvbnMKCiogUHV0IHlvdXIgbmFtZSBhdCB0aGUgdG9wIG9mIHRoZSBkb2N1bWVudC4gCgoqICoqRm9yIEFMTCBncmFwaHMsIHlvdSBzaG91bGQgaW5jbHVkZSBhcHByb3ByaWF0ZSBsYWJlbHMuKiogCgoqIEZlZWwgZnJlZSB0byBjaGFuZ2UgdGhlIGRlZmF1bHQgdGhlbWUsIHdoaWNoIEkgY3VycmVudGx5IGhhdmUgc2V0IHRvIGB0aGVtZV9taW5pbWFsKClgLiAKCiogVXNlIGdvb2QgY29kaW5nIHByYWN0aWNlLiBSZWFkIHRoZSBzaG9ydCBzZWN0aW9ucyBvbiBnb29kIGNvZGUgd2l0aCBbcGlwZXNdKGh0dHBzOi8vc3R5bGUudGlkeXZlcnNlLm9yZy9waXBlcy5odG1sKSBhbmQgW2dncGxvdDJdKGh0dHBzOi8vc3R5bGUudGlkeXZlcnNlLm9yZy9nZ3Bsb3QyLmh0bWwpLiAKCiogV2hlbiB5b3UgYXJlIGZpbmlzaGVkIHdpdGggQUxMIHRoZSBleGVyY2lzZXMsIHVuY29tbWVudCB0aGUgb3B0aW9ucyBhdCB0aGUgdG9wIHNvIHlvdXIgZG9jdW1lbnQgbG9va3MgbmljZXIuIERvbid0IGRvIGl0IGJlZm9yZSB0aGVuLCBvciBlbHNlIHlvdSBtaWdodCBtaXNzIHNvbWUgaW1wb3J0YW50IHdhcm5pbmdzIGFuZCBtZXNzYWdlcy4KCiMjIFdhcm0tdXAgZXhlcmNpc2VzIHdpdGggZ2FyZGVuIGRhdGEKClRoZXNlIGV4ZXJjaXNlcyB3aWxsIHJlaXRlcmF0ZSB0aGUgcGxvdHRpbmcgYW5kIGRhdGEgd3JhbmdsaW5nIHNraWxscyB5b3UgbGVhcm5lZCBpbiB0aGUgZ2dwbG90X2RwbHlyIHR1dG9yaWFsLiBUaGV5IHdpbGwgdXNlIHRoZSBgZ2FyZGVuX2hhcnZlc3RgIGRhdGEgdGhhdCB5b3UgYXJlIGhvcGVmdWxseSBzdGFydGluZyB0byBiZWNvbWUgZmFtaWxpYXIgd2l0aC4gCgogIDEuIEZpbHRlciB0aGUgZGF0YSB0byBgbGV0dHVjZWAgdmVnZXRhYmxlcy4gQ3JlYXRlIGEgaGlzdG9ncmFtIG9mIHRoZSB3ZWlnaHQgaW4gZ3JhbXMuIFRoaXMgc2hvd3MgdGhlIGRpc3RyaWJ1dGlvbiBvZiBsZXR0dWNlICJoYXJ2ZXN0cyIuIFRoZXJlIG1heSBiZSBtdWx0aXBsZSBoYXJ2ZXN0cyBpbiB0aGUgc2FtZSBkYXkgc2luY2UgdGhlcmUgYXJlIG11bHRpcGxlIHZhcmlldGllcyBvZiBsZXR0dWNlIGFuZCBJIHNvbWV0aW1lcyBoYXJ2ZXN0ZWQgbGV0dHVjZSBtb3JlIHRoYW4gb25jZSBhIGRheS4gRm9yIHRoZSBsZXR0dWNlLCBhbHNvIGNvbXB1dGUgdGhlIG1lYW4gYW5kIHN0YW5kYXJkIGRldmlhdGlvbiBvZiB0aGUgaGFydmVzdCAodXNlIHRoZSBgc3VtbWFyaXplKClgIGZ1bmN0aW9uKS4gVXNlIHRob3NlIHRvIGhlbHAgeW91IGRlc2NyaWJlIHRoZSBkaXN0cmlidXRpb24gb2YgbGV0dHVjZSBoYXJ2ZXN0cy4gCiAgCmBgYHtyfQpnYXJkZW5faGFydmVzdCAlPiUgCiAgZmlsdGVyKHZlZ2V0YWJsZSA9PSAibGV0dHVjZSIpICU+JSAKICBncm91cF9ieSh2ZWdldGFibGUpICU+JSAKICBzdW1tYXJpemUobWVhbiA9IG1lYW4od2VpZ2h0KSwgc3RkZXYgPSBzZCh3ZWlnaHQpKQogIApnYXJkZW5faGFydmVzdCAlPiUgIAogIGZpbHRlcih2ZWdldGFibGUgPT0gImxldHR1Y2UiKSAlPiUgCiAgZ3JvdXBfYnkodmVnZXRhYmxlKSAlPiUgCiAgZ2dwbG90KGFlcyh4ID0gd2VpZ2h0KSkgKwogIGdlb21faGlzdG9ncmFtKGZpbGwgPSAibGlnaHQgYmx1ZSIsIGNvbG9yID0gImJsYWNrIikgKwogIGxhYnModGl0bGUgPSAiVGhlIFdlaWdodCBvZiBMZXR0dWNlIEhhcnZlc3QiLAogICAgICAgeCA9ICJXZWlnaHQgKGdyYW1zKSIsCiAgICAgICB5ID0gIlRvdGFsIEFtb3VudCIpCmBgYAogCiBUaGUgYXZlcmFnZSB3ZWlnaHQgb2YgbGV0dHVjZSBoYXJ2ZXN0IGlzIDc2IGdyYW1zLCB3aXRoIGEgcHJldHR5IGhpZ2ggc3RhbmRhcmQgZGV2aWF0aW9uIG9mIDU2IGdyYW1zLiBNb3N0IG9mIHRoZSB0aW1lIHRoZSBoYXJ2ZXN0IHdlaWdodCBpcyBsZXNzIHRoYW4gMTAwIGdyYW1zLgogIAogIAogIDIuIENyZWF0ZSBhIGJhciBjaGFydCB0aGF0IHNob3dzIHRoZSBudW1iZXIgb2YgdGltZXMgSSBoYXJ2ZXN0ZWQgdGhlIGRpZmZlcmVudCB2YXJpZXRpZXMgb2YgbGV0dHVjZS4gUHV0IHRoZSB2YXJpZXR5IG9uIHRoZSB5LWF4aXMgYW5kIGNvdW50IG9uIHgtYXhpcy4gQ0hBTExFTkdFOiBvcmRlciB0aGVtIHNvIHRoZSB2YXJpZXR5IEkgaGFydmVzdGVkIHRoZSBtb3N0IGlzIGF0IHRoZSB0b3AuIAogIApgYGB7cn0KZ2FyZGVuX2hhcnZlc3QgJT4lIAogIGZpbHRlcih2ZWdldGFibGUgPT0gImxldHR1Y2UiKSAlPiUgCiAgZ3JvdXBfYnkodmVnZXRhYmxlLCB2YXJpZXR5KSAlPiUgCiAgc3VtbWFyaXplKGNvdW50ID0gbigpKSAlPiUgCiAgYXJyYW5nZShkZXNjKGNvdW50KSkgJT4lIAogIGdncGxvdChhZXMoeCA9IGNvdW50LCB5ID0gZmN0X3Jlb3JkZXIodmFyaWV0eSwgY291bnQpKSkgKwogIGdlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiLCBmaWxsID0gImRhcmt0dXJxdW9pc2UiLCBjb2xvciA9ICJ3aGl0ZSIpICsgCiAgbGFicyh0aXRsZSA9ICJUaGUgSGFydmVzdCBvZiBFYWNoIFZhcmlldGllcyBvZiBMZXR0dWNlIiwKICAgICAgIHggPSAiQ291bnQiLAogICAgICAgeSA9ICJWYXJpZXR5IikKCmBgYAogIAogIDMuIEZpbHRlciB0aGUgZGF0YSB0byBgYmVldHNgLiBTdW1tYXJpemUgdGhlIGRhdGEgdG8gY29tcHV0ZSB0aGUgdG90YWwgd2VpZ2h0IGluIGdyYW1zIGhhcnZlc3RzIG9uIGVhY2ggZGF5IGZvciBlYWNoIHZhcmlldHkgb2YgYmVldC4gQWRkIHR3byBuZXcgdmFyaWFibGVzOiBhLiB3ZWlnaHQgaW4gcG91bmRzLCBiLiBjdW11bGF0aXZlIHdlaWdodCBpbiBwb3VuZHMuIENyZWF0ZSBhIGxpbmUgZ3JhcGggdGhhdCBzaG93cyB0aGUgY3VtdWxhdGl2ZSBoYXJ2ZXN0IGJ5IGRheSBmb3IgZWFjaCB2YXJpZXR5IG9mIGJlZXQsIHdpdGggYSBkaWZmZXJlbnQgY29sb3IgZm9yIGVhY2ggdmFyaWV0eS4gRGVzY3JpYmUgd2hhdCB5b3Ugc2VlLiBDSEFMTEVOR0U6IG1ha2UgdGhlIGxpbmUgY29sb3JzIGNvcnJlc3BvbmQgd2l0aCB0aGUgY29sb3JzIG9mIHRoZSBiZWV0cyAtIGEgcmVkZGlzaC1wdXJwbGUgY29sb3IgYW5kIGEgZGFyay15ZWxsb3cgY29sb3IuCiAgCmBgYHtyfQpnYXJkZW5faGFydmVzdCAlPiUgCiAgZmlsdGVyKHZlZ2V0YWJsZSA9PSAiYmVldHMiKSAlPiUgCiAgZ3JvdXBfYnkodmFyaWV0eSwgZGF0ZSkgJT4lIAogIG11dGF0ZSh3ZWlnaHRfbGIgPSB3ZWlnaHQgKiAwLjAwMjIwNDYyLAogICAgICAgICB0b3Rfd2VpZ2h0X2xiID0gc3VtKHdlaWdodF9sYiksCiAgICAgICAgIGN1bV93ZWlnaHRfbGIgPSBjdW1zdW0odG90X3dlaWdodF9sYikpICU+JSAKICBnZ3Bsb3QoYWVzKHggPSBkYXRlLCB5ID0gY3VtX3dlaWdodF9sYiwgY29sb3IgPSB2YXJpZXR5KSkgKyAKICBnZW9tX2xpbmUoKSArIAogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXM9YygiI0Q4OTAwMCIsICIjMDBCQTQyIiwgIiNFNzZCRjMiKSkgKwogIGxhYnModGl0bGUgPSAiQ3VtdWxhdGl2ZSBIYXJ2ZXN0IGJ5IERheSBmb3IgRWFjaCBWYXJpZXR5IG9mIEJlZXQiLAogICAgICAgeCA9ICJEYXRlIiwKICAgICAgIHkgPSAiQ3VtdWxhdGl2ZSBXZWlnaHQgKGxiKSIpCmBgYAogIAogIDQuIFN1bW1hcml6ZSB0aGUgZGF0YSB0byBjb21wdXRlIHRoZSBkYWlseSBoYXJ2ZXN0IGluIHBvdW5kcyBmb3IgZWFjaCB2ZWdldGFibGUuIENyZWF0ZSBzaWRlLWJ5LXNpZGUgYm94cGxvdHMgdG8gY29tcGFyZSB0aGUgZGlzdHJpYnV0aW9ucyBvZiBkYWlseSBoYXJ2ZXN0cyBmb3IgZWFjaCB2ZWdldGFibGUuIAogIApgYGB7cn0KZ2FyZGVuX2hhcnZlc3QgJT4lIAogIGdyb3VwX2J5KHZlZ2V0YWJsZSwgZGF0ZSkgJT4lIAogIG11dGF0ZSh3ZWlnaHRfbGIgPSB3ZWlnaHQgKiAwLjAwMjIwNDYyKSAlPiUgCiAgc3VtbWFyaXNlKGRhaWx5X2hhcnZlc3RfbGIgPSBzdW0od2VpZ2h0X2xiKSkgJT4lIAogIGdncGxvdChhZXMoeCA9IGRhaWx5X2hhcnZlc3RfbGIsIHkgPSB2ZWdldGFibGUpKSArCiAgZ2VvbV9ib3hwbG90KCkgKwogIGxhYnModGl0bGUgPSAiRGFpbHkgSGFydmVzdCBpbiBQb3VuZHMgZm9yIEVhY2ggVmVnZXRhYmxlIiwKICAgICAgIHggPSAiRGFpbHkgSGFydmVzdCAobGIpIiwKICAgICAgIHkgPSAiVmVnZXRhYmxlIikKYGBgCgojIyBCYWJ5bmFtZXMgZXhlcmNpc2VzCgpXZSBhcmUgZ29pbmcgdG8gcHJhY3RpY2UgbW9yZSBgZHBseXJgIGFuZCBgZ2dwbG90MmAgc2tpbGxzIG9uIHRoZSBgYmFieW5hbWVzYCBkYXRhc2V0IGZyb20gdGhlIGBiYWJ5bmFtZXNgIGxpYnJhcnkuIEZpcnN0LCBpbnN0YWxsIHRoZSBwYWNrYWdlICh1bmxlc3MgeW91J3JlIHVzaW5nIHRoZSBzZXJ2ZXIsIGluIHdoaWNoIGNhc2UgaXQgaXMgYWxyZWFkeSBpbnN0YWxsZWQpLiBUaGVuLCBpbiB0aGUgbGlicmFyaWVzIHNlY3Rpb24gYXQgdGhlIHRvcCwgdW5jb21tZW50IHRoZSBsaW5lIHdoZXJlIHRoYXQgbGlicmFyeSBpcyBsb2FkZWQgYW5kIHJlLXJ1biB0aGF0IGNvZGUgY2h1bmsuIEJUVywgdGhpcyBpcyBteSBraWRzJyBmYXZvcml0ZSBkYXRhc2V0IC4uLiB5ZXMsIG15IGtpZHMgaGF2ZSBhIGZhdm9yaXRlIGRhdGFzZXQuCgo8YmxvY2txdW90ZSBjbGFzcz0idHdpdHRlci10d2VldCI+PHAgbGFuZz0iZW4iIGRpcj0ibHRyIj7igJxNb20sIGNhbiB3ZSBwbGVhc2UgZ3JhcGggdGhlIG5hbWVzIGRhdGE/4oCdIFllcywgeWVzIHdlIGNhbi4gPGEgaHJlZj0iaHR0cHM6Ly90d2l0dGVyLmNvbS9oYXNodGFnL3JzdGF0cz9zcmM9aGFzaCZhbXA7cmVmX3NyYz10d3NyYyU1RXRmdyI+I3JzdGF0czwvYT4gPGEgaHJlZj0iaHR0cHM6Ly90d2l0dGVyLmNvbS9oYXNodGFnL2Z1dHVyZWRhdGFzY2llbnRpc3Q/c3JjPWhhc2gmYW1wO3JlZl9zcmM9dHdzcmMlNUV0ZnciPiNmdXR1cmVkYXRhc2NpZW50aXN0PC9hPiA8YSBocmVmPSJodHRwczovL3R3aXR0ZXIuY29tL2hhc2h0YWcvZGF0YT9zcmM9aGFzaCZhbXA7cmVmX3NyYz10d3NyYyU1RXRmdyI+I2RhdGE8L2E+IDxhIGhyZWY9Imh0dHBzOi8vdHdpdHRlci5jb20vaGFzaHRhZy9iYWJ5bmFtZXM/c3JjPWhhc2gmYW1wO3JlZl9zcmM9dHdzcmMlNUV0ZnciPiNiYWJ5bmFtZXM8L2E+IDxhIGhyZWY9Imh0dHBzOi8vdC5jby9MR2dFUVJDcE4xIj5waWMudHdpdHRlci5jb20vTEdnRVFSQ3BOMTwvYT48L3A+Jm1kYXNoOyBsaXNhIGxlbmR3YXkgKEBsaXNhbGVuZHdheSkgPGEgaHJlZj0iaHR0cHM6Ly90d2l0dGVyLmNvbS9saXNhbGVuZHdheS9zdGF0dXMvMTA0MTUxNDYxMjc4NzY4NzQyNT9yZWZfc3JjPXR3c3JjJTVFdGZ3Ij5TZXB0ZW1iZXIgMTcsIDIwMTg8L2E+PC9ibG9ja3F1b3RlPiA8c2NyaXB0IGFzeW5jIHNyYz0iaHR0cHM6Ly9wbGF0Zm9ybS50d2l0dGVyLmNvbS93aWRnZXRzLmpzIiBjaGFyc2V0PSJ1dGYtOCI+PC9zY3JpcHQ+CgpBIGNvdXBsZSBub3RlcyBhYm91dCB0aGlzIGRhdGFzZXQ6CgphLiBBcyB3aXRoIG1hbnkgZGF0YXNldHMgdGhhdCBjb250YWluIHRoZSB2YXJpYWJsZSBzZXgsIGluIHRoaXMgZGF0YXNldCBpdCBpcyBiaW5hcnkuIEhvcGVmdWxseSB0aGlzIHdpbGwgY2hhbmdlIGluIHRoZSBmdXR1cmUuICAKYi4gT25seSBuYW1lcyB3aXRoIGF0IGxlYXN0IDUgdXNlcyBpbiBhIHllYXIgYXJlIGluY2x1ZGVkLiAgICAKYy4gVGhlIGRhdGEgY29tZSBmcm9tIHRoZSBTb2NpYWwgU2VjdXJpdHkgQWRtaW5pc3RyYXRpb24uIEluIHRoZSBwYXN0LCBub3QgZXZlcnlvbmUgbmVlZGVkIG9yIHdhcyBhYmxlIHRvIG9idGFpbiBhIHNvY2lhbCBzZWN1cml0eSBudW1iZXIuIFNvIHRob3NlIHBlb3BsZSBhcmUgbm90IHJlZmxlY3RlZCBpbiB0aGUgZGF0YS4KCiAgNS4gQWRkIGEgbmV3IHZhcmlhYmxlIHRvIGBiYWJ5bmFtZXNgIGNhbGxlZCBgaGFzMjAwMGAgdGhhdCBpbmRpY2F0ZXMgd2hldGhlciB0aGUgbmFtZSB3YXMgdXNlZCBtb3JlIHRoYW4gMjAwMCB0aW1lcyB3aXRoaW4gZWFjaCBzZXggYW5kIHllYXIuCiAgCmBgYHtyfQpiYWJ5bmFtZXMgJT4lIAogIG11dGF0ZShoYXMyMDAwID0gbiA+IDIwMDApCmBgYAoKICA2LiBBZGQgb24gdG8gdGhlIGNvZGUgeW91IHdyb3RlIGFib3ZlIGFuZCBjb21wdXRlIHRoZSBwcm9wb3J0aW9uIG9mIG5hbWVzIGVhY2ggeWVhciB0aGF0IGhhZCBtb3JlIHRoYW4gMjAwMCBiYWJpZXMuIERvIHRoaXMgc2VwYXJhdGVseSBmb3IgZWFjaCBzZXguIFRJUDogeW91IGNhbiB1c2UgYFRSVUVgcyBhbmQgYEZBTFNFYHMgbWF0aGVtYXRpY2FsbHkuIGBUUlVFYCA9IDEgYW5kIGBGQUxTRWAgPSAwLgogIApgYGB7cn0KYmFieW5hbWVzICU+JSAKICBtdXRhdGUoaGFzMjAwMCA9IG4gPiAyMDAwKSAlPiUgCiAgZ3JvdXBfYnkoeWVhciwgc2V4KSAlPiUgCiAgc3VtbWFyaXNlKGFib3ZlXzJrX3Byb3AgPSBtZWFuKGhhczIwMDApKSAjIHN1bShoYXMyMDAwKSAvIG4oKQpgYGAKICAKICA3LiBDcmVhdGUgYSBsaW5lIGdyYXBoIGZyb20gdGhlIGRhdGFzZXQgeW91IGNyZWF0ZWQgaW4gdGhlIHByZXZpb3VzIHByb2JsZW0uIFllYXIgc2hvdWxkIGJlIGFsb25nIHRoZSB4LWF4aXMsIHByb3BvcnRpb24gb2YgbmFtZXMgd2l0aCBtb3JlIHRoYW4gMjAwMCBiYWJpZXMgaXMgb24gdGhlIHktYXhpcywgYW5kIHRoZXJlIHNob3VsZCBiZSBhIHNlcGFyYXRlIGxpbmUgZm9yIGVhY2ggc2V4LgogIApgYGB7cn0KYmFieW5hbWVzICU+JSAKICBtdXRhdGUoaGFzMjAwMCA9IG4gPiAyMDAwKSAlPiUgCiAgZ3JvdXBfYnkoeWVhciwgc2V4KSAlPiUgCiAgc3VtbWFyaXNlKGFib3ZlXzJrX3Byb3AgPSBtZWFuKGhhczIwMDApKSAlPiUgCiAgZ2dwbG90KGFlcyh4ID0geWVhciwgeSA9IGFib3ZlXzJrX3Byb3AsIGNvbG9yID0gc2V4KSkgKyAKICBnZW9tX2xpbmUoKSArIAogIGxhYnModGl0bGUgPSAiUHJvcG9ydGlvbiBvZiBOYW1lcyB3aXRoIE1vcmUgVGhhbiAyMDAwIEJhYmllcyBvdmVyIFllYXJzIiwKICAgICAgIHggPSAiWWVhciIsCiAgICAgICB5ID0gIlByb3BvcnRpb24iKQpgYGAKCiAgOC4gRmluZCB0aGUgbW9zdCBwb3B1bGFyIG5hbWVzIGZvciBtYWxlcywgb3ZlciBhbGwgeWVhcnMgaW4gdGhlIGRhdGEsIGFuZCBvcmRlcmVkIGJ5IHBvcHVsYXJpdHkuIAoKYGBge3J9CmJhYnluYW1lcyAlPiUgCiAgZmlsdGVyKHNleCA9PSAiTSIpICU+JQogIGdyb3VwX2J5KG5hbWUpICU+JSAKICBzdW1tYXJpc2UodG90YWxfbiA9IHN1bShuKSkgJT4lIAogIGFycmFuZ2UoZGVzYyh0b3RhbF9uKSkKYGBgCgogIDkuIEZpbmQgdGhlIG1vc3QgcG9wdWxhciBuYW1lcyBmb3IgZmVtYWxlcywgb3ZlciBhbGwgeWVhcnMgaW4gdGhlIGRhdGEsIGFuZCBvcmRlcmVkIGJ5IHBvcHVsYXJpdHkuCgpgYGB7cn0KYmFieW5hbWVzICU+JSAKICBmaWx0ZXIoc2V4ID09ICJGIikgJT4lIAogIGdyb3VwX2J5KG5hbWUpICU+JSAKICBzdW1tYXJpc2UodG90YWxfbiA9IHN1bShuKSkgJT4lIAogIGFycmFuZ2UoZGVzYyh0b3RhbF9uKSkKYGBgCgogIDEwLiBGb3IgZWFjaCB5ZWFyIGFuZCBzZXggaW4gdGhlIGRhdGEsIGZpbmQgdGhlIG51bWJlciBvZiBkaXN0aW5jdC91bmlxdWUgbmFtZXMuIEZvciBleGFtcGxlLCBpZiBpbiAxODg5IGFsbCBtYWxlcyB3ZXJlIG5hbWVkIE1hdHRoZXcsIENocmlzdG9waGVyLCBvciBKb2huLCB0aGVuIHRoZXJlIHdvdWxkIGJlIDMgZGlzdGluY3QgbmFtZXMgZm9yIG1hbGVzIGluIDE4ODkgKGluIHJlYWxpdHksIHRoZXJlIGFyZSBtb3JlKS4gR3JhcGggdGhlIHJlc3VsdHMgYW5kIGRlc2NyaWJlIHdoYXQgeW91IHNlZS4KICAKYGBge3J9CmJhYnluYW1lcyAlPiUgCiAgZ3JvdXBfYnkoeWVhciwgc2V4KSAlPiUgCiAgc3VtbWFyaXNlKGRpc3RpbmN0X25hbWVzID0gbigpKSAlPiUgCiAgZ2dwbG90KGFlcyh4ID0geWVhciwgeSA9IGRpc3RpbmN0X25hbWVzLCBjb2xvciA9IHNleCkpICsgCiAgZ2VvbV9saW5lKCkgKwogIGxhYnModGl0bGUgPSAiVW5pcXVlIE5hbWVzIGZvciBFYWNoIFllYXIiKQpgYGAKCiAgMTEuIChDSEFMTEVOR0UpIEluIHRoaXMgZXhlcmNpc2UsIHdlIHdhbnQgdG8gZmluZCBvdXQgaG93IHVzaW5nICJwb3B1bGFyIiBuYW1lcyBoYXMgY2hhbmdlZCBvdmVyIHRpbWUuIEZvciBlYWNoIHllYXIgYW5kIHNleCwgZmluZCB0aGUgcHJvcG9ydGlvbiBvZiBuYW1lcyB0aGF0IGFyZSBpbiB0aGUgdG9wIDEwIG5hbWVzLiBVc2UgdGhpcyB0byBjcmVhdGUgYSBsaW5lIGdyYXBoIHdpdGggYSBkaWZmZXJlbnQgbGluZSBmb3IgZWFjaCBzZXguIFdoYXQgZG8geW91IG9ic2VydmU/IEhJTlQ6IFVzZSB0aGUgYHRvcF9uKClgIGZ1bmN0aW9uLiBUYWtlIHRpbWUgdG8gd29yayB0aHJvdWdoIHRoZSBzbWFsbCBzdGVwcyBhbmQgd3JpdGUgb3V0IHdoYXQgaXQgaXMgeW91IG5lZWQgdG8gZG8gaW4gd29yZHMgYmVmb3JlIGp1bXBpbmcgaW50byB0aGUgY29kZS4KICAKYGBge3J9CmJhYnluYW1lcyAlPiUgCiAgZ3JvdXBfYnkoeWVhciwgc2V4KSAlPiUgCiAgdG9wX24oMTAsIHd0ID0gbikgJT4lIAogIHN1bW1hcmlzZSh0b3RfcHJvcCA9IHN1bShwcm9wKSkgJT4lIAogIGdncGxvdChhZXMoeCA9IHllYXIsIHkgPSB0b3RfcHJvcCwgY29sb3IgPSBzZXgpKSArCiAgZ2VvbV9saW5lKCkgKyAKICBsYWJzKHRpdGxlID0gIlByb3BvcnRpb24gb2YgTmFtZXMgVGhhdCBBcmUgSW4gVGhlIFRvcCAxMCBOYW1lcyIsCiAgICAgICB5ID0gInRvdGFsIHByb3AiKQpgYGAKCiMjIENPVklELTE5IGV4ZXJjaXNlcwoKVGhlc2UgZXhlcmNpc2VzIHdpbGwgdXNlIGRhdGEgb24gY29yb25hdmlydXMgY2FzZXMgaW4gdGhlIFVTIHByb3ZpZGVkIGJ5IHRoZSBOZXcgWW9yayBUaW1lcywgd2hpY2ggSSBoYXZlIGNhbGxlZCBgY292aWQxOWAuIFlvdSBjYW4gKGFuZCBzaG91bGQpIHJlYWQgZGV0YWlscyBhYm91dCB0aGUgZGF0YSBbaGVyZV0oaHR0cHM6Ly9naXRodWIuY29tL255dGltZXMvY292aWQtMTktZGF0YSkuIFdlIHdpbGwgYmUgdXNpbmcgdGhlIHN0YXRlLWxldmVsIGRhdGEsIHdoaWNoIGluY2x1ZGVzIHRoZSBjdW11bGF0aXZlIG51bWJlciBvZiBjYXNlcyBmb3IgZWFjaCBzdGF0ZSBmb3IgZWFjaCBkYXRlLCBzdGFydGluZyBvbiB0aGUgZGF0ZSB0aGV5IGhhZCB0aGVpciBmaXJzdCBjYXNlLiAKCkluIHRoaXMgY2xhc3MsIEkgdGhpbmsgaXQgaXMgaW1wb3J0YW50IHRoZSB3ZSBhZGRyZXNzIGFuZCB1c2UgcmVsZXZhbnQgZGF0YSwgZXZlbiBpZiBpdCBpcyBjaGFsbGVuZ2luZy4gVGhhdCBiZWluZyBzYWlkLCBJIGRvIG5vdCB3YW50IHRoZXNlIGRhdGEgdG8gaW5jcmVhc2UgeW91ciBzdHJlc3MgbGV2ZWwuIElmIHlvdSB3b3VsZCBwcmVmZXIgbm90IHRvIHdvcmsgd2l0aCB0aGVzZSBkYXRhLCBwbGVhc2UgbGV0IG1lIGtub3cgYW5kIEkgd2lsbCBwcm92aWRlIGFsdGVybmF0aXZlIGV4ZXJjaXNlcy4KCiAgMTIuIEZpbmQgdGhlIGRhdGUgb2YgZWFjaCBzdGF0ZSdzIGZpcnN0IGNhc2UuIE9yZGVyIHRoZSBzdGF0ZXMgZnJvbSBsYXRlc3QgdG8gbW9zdCByZWNlbnQgZmlyc3QgY2FzZXMuICAKICAKYGBge3J9CmNvdmlkMTkgJT4lIAogIGdyb3VwX2J5KHN0YXRlKSAlPiUgCiAgc3VtbWFyaXNlKGZpcnN0X2Nhc2UgPSBtaW4oZGF0ZSkpICU+JSAKICBhcnJhbmdlKGZpcnN0X2Nhc2UpCmBgYAoKICAxMy4gTGV0J3MgZXhhbWluZSBkYXRhIGZvciBNaW5uZXNvdGEsIFdpc2NvbnNpbiwgSW93YSwgTm9ydGggRGFrb3RhLCBhbmQgU291dGggRGFrb3RhLiBDcmVhdGUgYSBsaW5lIGdyYXBoIG9mIGN1bXVsYXRpdmUgY2FzZSBjb3VudCBieSBkYXRlIGFuZCBjb2xvciB0aGUgbGluZXMgYnkgc3RhdGUuIFdoYXQgZG8geW91IG9ic2VydmU/IElzIHRoaXMgYSAiZmFpciIgY29tcGFyaXNvbiwgZG8geW91IHRoaW5rPwogIApgYGB7cn0KY292aWQxOSAlPiUgCiAgZmlsdGVyKHN0YXRlICVpbiUgYygiTWlubmVzb3RhIiwiV2lzY29uc2luIiwiSW93YSIsIk5vcnRoIERha290YSIsIlNvdXRoIERha290YSIpKSAlPiUgCiAgZ3JvdXBfYnkoc3RhdGUsIGRhdGUpICU+JSAKICBzdW1tYXJpemUodG90YWxfY2FzZSA9IGN1bXN1bShjYXNlcykpICU+JSAKICBnZ3Bsb3QoYWVzKHggPSBkYXRlLCB5ID0gdG90YWxfY2FzZSwgY29sb3IgPSBzdGF0ZSkpICsKICBnZW9tX2xpbmUoKSArIAogIGxhYnModGl0bGUgPSAiQ3VtdWxhdGl2ZSBDYXNlIENvdW50IikKYGBgCgogSSBzYXcgYSBtb2RlbCBvZiBleHBvbmVudGlhbCBncm93dGggZm9yIGVhY2ggc3RhdGUuIE1heWJlIGl0J3Mgbm90IGEgZmFpciBjb21wZXRpdGlvbiBiZWNhdXNlIGVhY2ggc3RhdGUgaGFzIGRpZmZlcmVudCBudW1iZXIgb2YgcG9wdWxhdGlvbi4gQSBiZXR0ZXIgd2F5IGlzIHRvIGNvbXBhcmUgdGhlIHByb3BvcnRpb24gb2YgY2FzZXMgYW1vbmcgcG9wdWxhdGlvbi4KCiAgMTQuIENyZWF0ZSB0aGUgc2FtZSBwbG90IGFzIGFib3ZlLCBidXQgbWFrZSB0aGUgeS1heGlzIGxvZyBzY2FsZSBieSB1c2luZyB0aGUgYHNjYWxlX3lfbG9nMTAoKWAgZnVuY3Rpb24gKHNlYXJjaCBmb3IgaXQgaW4gSGVscCBvciBydW4gYD9zY2FsZV95X2xvZzEwKClgIGluIHRoZSBjb25zb2xlKS4gV2hhdCBkbyB5b3Ugb2JzZXJ2ZT8gSXMgdGhpcyBhICJmYWlyIiBjb21wYXJpc29uPyBDb25zaWRlciB0aGlzIHBsb3QgYW5kIHRoZSBwcmV2aW91cyBvbmUsIGhvdyBlbHNlIG1pZ2h0IHlvdSBpbXByb3ZlIHRoZW0/CiAgCmBgYHtyfQpjb3ZpZDE5ICU+JSAKICBmaWx0ZXIoc3RhdGUgJWluJSBjKCJNaW5uZXNvdGEiLCJXaXNjb25zaW4iLCJJb3dhIiwiTm9ydGggRGFrb3RhIiwiU291dGggRGFrb3RhIikpICU+JSAKICBncm91cF9ieShzdGF0ZSwgZGF0ZSkgJT4lIAogIHN1bW1hcml6ZSh0b3RhbF9jYXNlID0gY3Vtc3VtKGNhc2VzKSkgJT4lIAogIGdncGxvdChhZXMoeCA9IGRhdGUsIHkgPSB0b3RhbF9jYXNlLCBjb2xvciA9IHN0YXRlKSkgKwogIGdlb21fbGluZSgpICsgCiAgc2NhbGVfeV9sb2cxMCgpICsKICBsYWJzKHRpdGxlID0gIkN1bXVsYXRpdmUgQ2FzZSBDb3VudCIpCmBgYAogCiBPbiB0aGUgbG9nZ2VkIHkgYXhpcywgbW92aW5nIG9uZSB1bml0IGlzIGxpa2UgbXVsdGlwbHlpbmcgYnkgdGVuLiBUaGlzIHNlY29uZCBncmFwaCBhbHNvIHJlZmxlY3RzIHRoZSByYXRlIG9mIGNoYW5nZS4KCiAgMTUuIEluIHRoaXMgZXhlcmNpc2Ugd2UgYXJlIGdvaW5nIHRvIGNvbXB1dGUgc29tZSBuZXcgdmFyaWFibGVzIGFuZCBnZXQgc2V0IHVwIGZvciB0aGUgbmV4dCBleGVyY2lzZS4gRm9yIGVhY2ggc3RhdGUsIGNvbXB1dGUgYSAxLWRheSBsYWcgYW5kIDctZGF5IGxhZyBvZiBjdW11bGF0aXZlIGNvdW50cyAodXNlIHRoZSBgbGFnKClgIGZ1bmN0aW9uKS4gV2hlbiB5b3UgZG8gdGhpcywgdGhlcmUgd2lsbCBiZSBzb21lIG1pc3NpbmcgdmFsdWVzIGJlY2F1c2UgYXQgdGhlIGJlZ2lubmluZyB0aGVyZSBhcmUgbm8gbGFncy4gVXNlIHRoZSBgcmVwbGFjZV9uYSgpYCBmdW5jdGlvbiB0byByZXBsYWNlIHRob3NlIG1pc3NpbmcgdmFsdWVzIHdpdGggMCdzLiBBZGQgdHdvIG5ldyB2YXJpYWJsZXM6IGEuIGEgdmFyaWFibGUgdGhhdCBjb21wdXRlcyB0aGUgbnVtYmVyIG9mIG5ldyBjYXNlcyBmb3IgZWFjaCBkYXksIHdoaWNoIGNhbiBiZSBjb21wdXRlZCBieSB0YWtpbmcgdGhlIGN1cnJlbnQgY2FzZXMgbWludXMgdGhlIDEtZGF5IGxhZy4gYi4gYSB2YXJpYWJsZSB0aGF0IGNvbXB1dGVzIHRoZSA3IGRheSBhdmVyYWdlLCB3aGljaCBjYW4gYmUgY29tcHV0ZWQgYnkgdGFraW5nIHRoZSBjdXJyZW50IGNhc2VzIG1pbnVzIHRoZSA3LWRheSBsYWcgYW5kIGRpdmlkaW5nIHRoYXQgYnkgNy4gCiAgCmBgYHtyfQpjb3ZpZF9sYWcgPC0KICBjb3ZpZDE5ICU+JSAKICAgIGdyb3VwX2J5KHN0YXRlKSAlPiUgCiAgICBtdXRhdGUob25lX2RheV9sYWcgPSBsYWcoY2FzZXMsIDEpLCAKICAgICAgICAgICBzZXZlbl9kYXlfbGFnID0gbGFnKGNhc2VzLCA3KSwKICAgICAgICAgICBvbmVfZGF5X2xhZyA9IHJlcGxhY2VfbmEob25lX2RheV9sYWcsIDApLAogICAgICAgICAgIHNldmVuX2RheV9sYWcgPSByZXBsYWNlX25hKHNldmVuX2RheV9sYWcsIDApLAogICAgICAgICAgIG5ld2Nhc2VzX2RheSA9IGNhc2VzIC0gb25lX2RheV9sYWcsCiAgICAgICAgICAgc2V2ZW5fZGF5X2F2ZyA9IChjYXNlcyAtIHNldmVuX2RheV9sYWcpIC8gNykKCmNvdmlkX2xhZwpgYGAKICAKICAxNi4gVXNpbmcgdGhlIGRhdGEgeW91IGNyZWF0ZWQgaW4gdGhlIHByZXZpb3VzIHN0ZXAgKHlvdSBjYW4gYCU+JWAgdGhhdCBkYXRhIGludG8gYGdncGxvdCgpYCksIGNyZWF0ZSBhIHBsb3Qgd2l0aCB0aGUgZm9sbG93aW5nOiBhLiBmYWNldCBieSBzdGF0ZSwgYi4gYSBsaW5lIHdpdGggdGhlIG51bWJlciBvZiBuZXcgY2FzZXMgYnkgZGF0ZSwgYi4gYSBsaW5lIHdpdGggdGhlIDctZGF5IGF2ZXJhZ2UgbnVtYmVyIG9mIG5ldyBjYXNlcyBieSBkYXRlIC0gbWFrZSB0aGlzIGxpbmUgYmx1ZS4gV2hhdCBkbyB5b3Ugb2JzZXJ2ZT8gSG93IGNvdWxkIHRoaXMgZ3JhcGggYmUgaW1wcm92ZWQ/CiAgCmBgYHtyfQpjb3ZpZF9sYWcgJT4lIAogIGdncGxvdChhZXMoeCA9IGRhdGUpKSArIAogIGdlb21fbGluZShhZXMoeSA9IG5ld2Nhc2VzX2RheSksIGNvbG9yID0gInJlZCIpICsgCiAgZ2VvbV9saW5lKGFlcyh5ID0gc2V2ZW5fZGF5X2F2ZyksIGNvbG9yID0gImJsdWUiKSArCiAgZmFjZXRfd3JhcCh2YXJzKHN0YXRlKSkgKwogIGxhYnModGl0bGUgPSAiTnVtYmVyIG9mIE5ldyBDYXNlcyBhbmQgNyBEYXkgQXZnIE51bWJlciBvZiBOZXcgQ2FzZXMgYnkgRGF0ZSIpCmBgYAogIAogIEkgZmluZCBpbiBtb3N0IHN0YXRlIHRoZXJlIGFyZSBqdXN0IDIgc3RyYWlnaHQgbGluZXMuIFRoaXMgaXMgYmVjYXVzZSB3aGlsZSBzb21lIHN0YXRlIGhhcyBtdWNoIG1vcmUgcG9wdWxhdGlvbiwgdGhlIHkgYXhpcyBmb3IgYWxsIHRoZSBzdGF0ZXMgYXJlIHRoZSBzYW1lLCB3aGljaCBzaG91bGQgYmUgaW1wcm92ZWQuIAogIAogIDE3LiBGb3IgdGhpcyBwYXJ0LCB5b3Ugd2lsbCBuZWVkIHRoZSBgZ2VvZmFjZXRgIGxpYnJhcnkuIEZpcnN0LCBpbnN0YWxsIGl0ICh1bmxlc3MgeW91IGFyZSB1c2luZyB0aGUgc2VydmVyLCBpbiB3aGljaCBjYXNlIGl0IHdpbGwgYWxyZWFkeSBiZSB0aGVyZSBmb3IgeW91KS4gVGhlbiwgdW5jb21tZW50IHRoZSBsaWJyYXJ5IHN0YXRlbWVudCBpbiB0aGUgMm5kIFIgY29kZSBjaHVuayBhYm92ZSBhbmQgcmUtcnVuIHRoYXQgUiBjb2RlIGNodW5rLiBDcmVhdGUgdGhlIHNhbWUgcGxvdCBhcyBhYm92ZSBidXQgaW5zdGVhZCBvZiByZWd1bGFyIGZhY2V0aW5nLCB1c2UgYGZhY2V0X2dlbygpYC4gQWxzbyBzZXQgYHNjYWxlcz0iZnJlZSJgIHRvIHRoZSBmYWNldCBmdW5jdGlvbi4gV2hhdCBkbyB5b3UgbGVhcm4gZnJvbSB0aGlzIHBsb3QgdGhhdCB5b3UgZGlkbid0IGxlYXJuIGZyb20gdGhlIHByZXZpb3VzIG9uZT8KICAKYGBge3J9CmNvdmlkX2xhZyAlPiUgCiAgZ2dwbG90KGFlcyh4ID0gZGF0ZSkpICsgCiAgZ2VvbV9saW5lKGFlcyh5ID0gbmV3Y2FzZXNfZGF5KSwgY29sb3IgPSAicmVkIikgKyAKICBnZW9tX2xpbmUoYWVzKHkgPSBzZXZlbl9kYXlfYXZnKSwgY29sb3IgPSAiYmx1ZSIpICsKICBmYWNldF9nZW8odmFycyhzdGF0ZSksIHNjYWxlcyA9ICJmcmVlIikgKwogIGxhYnModGl0bGUgPSAiTnVtYmVyIG9mIE5ldyBDYXNlcyBhbmQgNyBEYXkgQXZnIE51bWJlciBvZiBOZXcgQ2FzZXMgYnkgRGF0ZSIpCmBgYAogIAogIEkgY291bGQgYWxzbyBzZWUgdGhlIGdlb2dyYXBoeSBvZiBlYWNoIGRhdGEgcG9pbnQgaW4gdGhpcyBzZWNvbmQgZ3JhcGguCiAgCgo=